package top.v5it.japi.ddd.core.dao.impl.wrapper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.lang.func.VoidFunc1;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * 子查询包装类
 *
 * @param <T> 实体类型
 * @param <W> 子类类型，用于方法链式调用
 * @date 2023/12/7
 */
public class SubSqlWrapper<T, W extends SubSqlWrapper<T, W>> extends SqlWrapper<T, W> {

    private final static String SELECT_TEMPLATE = "SELECT 1 FROM `{}` {}";
    private VoidFunc1<Map<String, Join>> voidFunc1;

    protected SubSqlWrapper(T entity) {
        super(entity);
    }

    /**
     * 添加关联子查询条件
     *
     * @param subColumnFunc    子查询列的获取函数
     * @param parentColumnFunc 父查询列的获取函数
     * @param <L>              子查询实体类型
     * @param <R>              父查询实体类型
     * @return 当前实例
     */
    public <L, R> W ref(Func1<L, ?> subColumnFunc, Func1<R, ?> parentColumnFunc) {
        final BiFunction<String, String, String> mapper = (t, u) -> StrUtil.format(COLUMN_TEMPLATE, t, u);
        final Class<L> subRealClass = LambdaUtil.getRealClass(subColumnFunc);
        final Class<R> parentRealClass = LambdaUtil.getRealClass(parentColumnFunc);
        for (Map.Entry<String, SqlWrapper.Join> entry : entities.entrySet()) {
            if (ClassUtil.isAssignable(entry.getValue().getEntity().getClass(), subRealClass)) {
                final String subFieldName = LambdaUtil.getFieldName(subColumnFunc);
                final String key = entry.getKey();
                final String left = mapper.apply(key, subFieldName);
                // 利用函数来延迟处理
                voidFunc1 = parentEntities -> {
                    for (Map.Entry<String, Join> parentEntry : parentEntities.entrySet()) {
                        if (ClassUtil.isAssignable(parentEntry.getValue().getEntity().getClass(), parentRealClass)) {
                            final String parentFieldName = LambdaUtil.getFieldName(parentColumnFunc);
                            final String right = mapper.apply(parentEntry.getKey(), parentFieldName);
                            condition.add(left + " = " + right);
                            break;
                        }
                    }
                };
                break;
            }
        }
        return wrapper;
    }

    @Override
    protected String getSelect(String as, String tableName) {
        final List<String> columnSegments = getColumnSegments();
        if (CollUtil.isEmpty(columnSegments)) return StrUtil.format(SELECT_TEMPLATE, tableName, as);
        return columnSegments.stream()
                .collect(Collectors.joining(", ", "SELECT ", " FROM `" + tableName + "` " + as + ""));
    }

    @Override
    protected String getAsName() {
        return "S";
    }

    @Override
    protected <T1, W1 extends SqlWrapper<T1, W1>> String getSql(SqlWrapper<T1, W1> wrapper) {
        if (Objects.nonNull(voidFunc1)) voidFunc1.callWithRuntimeException(wrapper.entities);
        wrapper.getValue().putAll(this.getValue());
        return super.getSql();
    }
}
